#if defined(_WIN32) || defined(_WIN64)
#include <winsock2.h>   //-lwsock32
#include <iphlpapi.h>   //-lIPHLPAPI
#include <ws2tcpip.h>
#include <windows.h>
#include <malloc.h>
#else
#include <netdb.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <ifaddrs.h>
#endif
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include "net_infom.h"

#if defined(_WIN32) || defined(_WIN64)
/**
 * 获取所有可用网卡信息
*/
int getLocalNetInfo(void)   
{
    int netCardNum = 0;
    PIP_ADAPTER_INFO pIpAdapterInfo = (PIP_ADAPTER_INFO)malloc(sizeof(IP_ADAPTER_INFO));
    unsigned long stSize = sizeof(IP_ADAPTER_INFO);
    int nRel = 0;
    nRel = GetAdaptersInfo(pIpAdapterInfo, &stSize);
    if (ERROR_BUFFER_OVERFLOW == nRel) {
        free(pIpAdapterInfo);
        pIpAdapterInfo = (PIP_ADAPTER_INFO)malloc(stSize);
        nRel = GetAdaptersInfo(pIpAdapterInfo, &stSize);
    }
    if (ERROR_SUCCESS == nRel) {
        while (pIpAdapterInfo) {
            printf("name: %s\n",pIpAdapterInfo->Description);
            printf("ip: %s\n", pIpAdapterInfo->IpAddressList.IpAddress.String);
            printf("gw: %s\n", pIpAdapterInfo->GatewayList.IpAddress.String);
            printf("mask: %s\n", pIpAdapterInfo->IpAddressList.IpMask.String);
            pIpAdapterInfo = pIpAdapterInfo->Next;
            netCardNum++;
            }
     }
    if (pIpAdapterInfo) {
        free(pIpAdapterInfo);
    }
    return 0;
}

/**
 * @brief Get the local network info object
 * 
 * @return int 
 * @note 这是一个参考示例，具体自己修改使用
 */
int get_local_network_info() {
    // 初始化Winsock
    WSADATA wsaData;
    int result = WSAStartup(MAKEWORD(2, 2), &wsaData);
    if (result != 0) {
        printf("WSAStartup failed with error: %d\n", result);
        return 1;
    }

    // 获取接口信息
    PIP_ADAPTER_ADDRESSES adapterAddresses = NULL;
    ULONG outBufLen = 0;
    DWORD flags = GAA_FLAG_INCLUDE_PREFIX;
    GetAdaptersAddresses(AF_UNSPEC, flags, NULL, adapterAddresses, &outBufLen);

    adapterAddresses = (IP_ADAPTER_ADDRESSES*)malloc(outBufLen);
    if (adapterAddresses == NULL) {
        printf("Error allocating memory needed to call GetAdaptersAddresses\n");
        WSACleanup();
        return 1;
    }

    DWORD ret = GetAdaptersAddresses(AF_UNSPEC, flags, NULL, adapterAddresses, &outBufLen);
    if (ret != NO_ERROR) {
        printf("GetAdaptersAddresses failed with error: %ld\n", ret);
        free(adapterAddresses);
        WSACleanup();
        return 1;
    }

    // 遍历获取到的适配器信息，并打印IP地址、子网掩码和网关
    PIP_ADAPTER_ADDRESSES adapter = adapterAddresses;
    while (adapter) {
        printf("Adapter Name: %S\n", adapter->FriendlyName);
        if (adapter->OperStatus == IfOperStatusUp) printf("Connection: On\n");  // 当前网卡连接状态
        else printf("Connection: OFF\n");
        PIP_ADAPTER_UNICAST_ADDRESS address = adapter->FirstUnicastAddress;
        while (address) {
            char ipAddr[100];
            getnameinfo(address->Address.lpSockaddr, address->Address.iSockaddrLength, ipAddr, sizeof(ipAddr), NULL, 0, NI_NUMERICHOST);
            // 在Windows系统中，如果网络适配器无法获取到DHCP服务器提供的有效IP地址时，
            // 会自动分配一个169.254.x.x的APIPA（Automatic Private IP Addressing）地址。
            // 这种情况通常发生在网络连接出现问题或者无法获取有效IP地址的情况下。
            if (strncmp(ipAddr, "169.254.", 8) != 0) {  // 过滤掉169.254.x.x的IP地址
                printf("  IP Address: %s\n", ipAddr);
                if (address->OnLinkPrefixLength <= 32) {  // IPv4地址
                    ULONG mask = ((1 << address->OnLinkPrefixLength) - 1) << (32 - address->OnLinkPrefixLength);
                    mask = htonl(mask);
                    // 下面有两个主要是因为大小端问题，具体自行测试
                    // printf("  Subnet Mask: %d.%d.%d.%d\n", (mask >> 24) & 0xff, (mask >> 16) & 0xff, (mask >> 8) & 0xff, mask & 0xff);
                    printf("  Subnet Mask: %d.%d.%d.%d\n", (mask >> 0) & 0xff, (mask >> 8) & 0xff, (mask >> 16) & 0xff, (mask >> 24) & 0xff);
                }
            }
            address = address->Next;
        }

        // 打印网关
        PIP_ADAPTER_GATEWAY_ADDRESS gateway = adapter->FirstGatewayAddress;
        while (gateway) {
            char ipAddr[100];
            getnameinfo(gateway->Address.lpSockaddr, gateway->Address.iSockaddrLength, ipAddr, sizeof(ipAddr), NULL, 0, NI_NUMERICHOST);
            printf("  Gateway: %s\n", ipAddr);
            gateway = gateway->Next;
        }

        adapter = adapter->Next;
    }

    free(adapterAddresses);
    WSACleanup();
    return 0;
}
#else   /* defined(_WIN32) || defined(_WIN64) */
#if !USE_IF_ADDR
/**
 * 功能：    获取网卡的ip地址
 * 参数：    eth_name 网卡的名称
 * 返回值：    ip地址
 * 备注：    get_local_ip("eth0");
*/
// char* get_local_ip(char* eth_name)
// {                                                                                           
//     int sock;
//     struct sockaddr_in sin;
//     struct ifreq ifr;
//     sock = socket(AF_INET, SOCK_DGRAM, 0); 
//     if (sock == -1) {
//             perror("socket");
//             return NULL;
//     }   
//     strncpy(ifr.ifr_name, eth_name, IFNAMSIZ);
//     ifr.ifr_name[IFNAMSIZ - 1] = 0;
//     if (ioctl(sock, SIOCGIFADDR, &ifr) < 0) {
//             perror("ioctl");
//             return NULL;
//     }   

//     memcpy(&sin, &ifr.ifr_addr, sizeof(sin));
//     return inet_ntoa(sin.sin_addr);
// }


int get_local_ip(const char* eth_name ,char* ip)
{
    int sockfd;
    struct ifreq ifr;
    //create socket
    sockfd = socket(AF_INET,SOCK_DGRAM,0);
    if(sockfd == -1)
    {
            perror("socket");
            return -1;
    }

    strncpy(ifr.ifr_name,eth_name,IFNAMSIZ);
    ifr.ifr_name[IFNAMSIZ - 1] = 0;
    if(ioctl(sockfd,SIOCGIFADDR,&ifr) == 0)
    {
        strcpy(ip,inet_ntoa(((struct sockaddr_in*)&(ifr.ifr_addr))->sin_addr));
        return 0;
    }
    else perror("ioctl");
    return -1;
}

int get_local_hwaddr(const char* eth_name ,char* hwaddr)
{
    int sockfd;
    struct ifreq ifr;
    //create socket
    sockfd = socket(AF_INET,SOCK_DGRAM,0);
    if(sockfd == -1)
    {
        perror("socket");
        return -1;
    }

    strncpy(ifr.ifr_name,eth_name,IFNAMSIZ);
    ifr.ifr_name[IFNAMSIZ - 1] = 0;
    if(ioctl(sockfd,SIOCGIFHWADDR,&ifr)==0)
    {
        memcpy(hwaddr,ifr.ifr_hwaddr.sa_data,6);
        return 0;
    }
    return -1;
}

int get_local_netmask(const char* eth_name ,char* netmask)
{
    int sockfd;
    struct ifreq ifr;
    //create socket
    sockfd = socket(AF_INET,SOCK_DGRAM,0);
    if(sockfd == -1)
    {
        perror("socket");
        return -1;
    }

    strncpy(ifr.ifr_name,eth_name,IFNAMSIZ);
    ifr.ifr_name[IFNAMSIZ - 1] = 0;
    if(ioctl(sockfd,SIOCGIFNETMASK,&ifr)==0)
    {
        strcpy(netmask,inet_ntoa(((struct sockaddr_in*)&(ifr.ifr_netmask))->sin_addr));
        return 0;
    }
    return -1;
}

int get_local_gw(const char* eth_name ,char* gw)
{
    int sockfd;
    struct ifreq ifr;
    //create socket
    sockfd = socket(AF_INET,SOCK_DGRAM,0);
    if(sockfd == -1)
    {
        perror("socket");
        return -1;
    }

    strncpy(ifr.ifr_name,eth_name,IFNAMSIZ);
    ifr.ifr_name[IFNAMSIZ - 1] = 0;
    if(ioctl(sockfd,SIOCGIFBRDADDR,&ifr)==0)
    {
        strcpy(gw,inet_ntoa(((struct sockaddr_in*)&(ifr.ifr_broadaddr))->sin_addr));
        return 0;
    }
    return -1;
}

int get_ip_by_gw(const char* gw,char *ip)
{
    return -1;
}

/**
 * 获取所有可用网卡信息
*/
int getLocalNetInfo(void)
{
    int fd;
    int interfaceNum = 0;
    struct ifreq buf[16];
    struct ifconf ifc;
    struct ifreq ifrcopy;
    char mac[16] = {0};
    char ip[32] = {0};
    char broadAddr[32] = {0};
    char subnetMask[32] = {0};

    if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
    {
        perror("socket");

        close(fd);
        return -1;
    }

    ifc.ifc_len = sizeof(buf);
    ifc.ifc_buf = (caddr_t)buf;
    if (!ioctl(fd, SIOCGIFCONF, (char *)&ifc))
    {
        interfaceNum = ifc.ifc_len / sizeof(struct ifreq);
        printf("interface num = %dn", interfaceNum);
        while (interfaceNum-- > 0)
        {
            printf("ndevice name: %sn", buf[interfaceNum].ifr_name);

            //ignore the interface that not up or not runing  
            ifrcopy = buf[interfaceNum];
            if (ioctl(fd, SIOCGIFFLAGS, &ifrcopy))
            {
                printf("ioctl: %s [%s:%d]n", strerror(errno), __FILE__, __LINE__);

                close(fd);
                return -1;
            }

            //get the mac of this interface  
            if (!ioctl(fd, SIOCGIFHWADDR, (char *)(&buf[interfaceNum])))
            {
                memset(mac, 0, sizeof(mac));
                snprintf(mac, sizeof(mac), "%02x%02x%02x%02x%02x%02x",
                    (unsigned char)buf[interfaceNum].ifr_hwaddr.sa_data[0],
                    (unsigned char)buf[interfaceNum].ifr_hwaddr.sa_data[1],
                    (unsigned char)buf[interfaceNum].ifr_hwaddr.sa_data[2],

                    (unsigned char)buf[interfaceNum].ifr_hwaddr.sa_data[3],
                    (unsigned char)buf[interfaceNum].ifr_hwaddr.sa_data[4],
                    (unsigned char)buf[interfaceNum].ifr_hwaddr.sa_data[5]);
                printf("device mac: %sn", mac);
            }
            else
            {
                printf("ioctl: %s [%s:%d]n", strerror(errno), __FILE__, __LINE__);
                close(fd);
                return -1;
            }

            //get the IP of this interface  

            if (!ioctl(fd, SIOCGIFADDR, (char *)&buf[interfaceNum]))
            {
                snprintf(ip, sizeof(ip), "%s",
                    (char *)inet_ntoa(((struct sockaddr_in *)&(buf[interfaceNum].ifr_addr))->sin_addr));
                printf("device ip: %sn", ip);
            }
            else
            {
                printf("ioctl: %s [%s:%d]n", strerror(errno), __FILE__, __LINE__);
                close(fd);
                return -1;
            }

            //get the broad address of this interface  

            if (!ioctl(fd, SIOCGIFBRDADDR, &buf[interfaceNum]))
            {
                snprintf(broadAddr, sizeof(broadAddr), "%s",
                    (char *)inet_ntoa(((struct sockaddr_in *)&(buf[interfaceNum].ifr_broadaddr))->sin_addr));
                printf("device broadAddr: %sn", broadAddr);
            }
            else
            {
                printf("ioctl: %s [%s:%d]n", strerror(errno), __FILE__, __LINE__);
                close(fd);
                return -1;
            }

            //get the subnet mask of this interface  
            if (!ioctl(fd, SIOCGIFNETMASK, &buf[interfaceNum]))
            {
                snprintf(subnetMask, sizeof(subnetMask), "%s",
                    (char *)inet_ntoa(((struct sockaddr_in *)&(buf[interfaceNum].ifr_netmask))->sin_addr));
                printf("device subnetMask: %sn", subnetMask);
            }
            else
            {
                printf("ioctl: %s [%s:%d]n", strerror(errno), __FILE__, __LINE__);
                close(fd);
                return -1;

            }
        }
    }
    else
    {
        printf("ioctl: %s [%s:%d]n", strerror(errno), __FILE__, __LINE__);
        close(fd);
        return -1;
    }

    close(fd);

    return 0;
}


#else   /* USE_IF_ADDR */ //此方法不可获取mac地址

/**
 * 获取所有可用网卡信息
*/
int getLocalInfo()
{
    struct sockaddr_in *sin = NULL;
    struct ifaddrs *ifa = NULL, *ifList;

    if (getifaddrs(&ifList) < 0)
    {
        return -1;
    }

    for (ifa = ifList; ifa != NULL; ifa = ifa->ifa_next)
    {
        if (ifa->ifa_addr == NULL) continue;
        if(ifa->ifa_addr->sa_family == AF_INET)     // have ipv4
        {
            printf("n>>> interfaceName: %s\n", ifa->ifa_name);

            sin = (struct sockaddr_in *)ifa->ifa_addr;
            printf(">>> ipAddress: %s\n", inet_ntoa(sin->sin_addr));

            sin = (struct sockaddr_in *)ifa->ifa_dstaddr;
            printf(">>> broadcast: %s\n", inet_ntoa(sin->sin_addr));

            sin = (struct sockaddr_in *)ifa->ifa_netmask;
            printf(">>> subnetMask: %s\n", inet_ntoa(sin->sin_addr));
        }
    }

    freeifaddrs(ifList);

    return -1;
}

int get_local_ip(const char* eth_name ,char* ip)
{
    struct sockaddr_in *sin = NULL;
    struct ifaddrs *ifa = NULL, *ifList;

    if (getifaddrs(&ifList) < 0)
    {
        return -1;
    }

    for (ifa = ifList; ifa != NULL; ifa = ifa->ifa_next)
    {
        if(ifa->ifa_addr->sa_family == AF_INET)
        {
            if(strcmp(eth_name,ifa->ifa_name) == 0)
            {
                sin = (struct sockaddr_in *)ifa->ifa_addr;
                strcpy(ip,inet_ntoa(sin->sin_addr));
                freeifaddrs(ifList);
                return 0;
            }
        }
    }

    freeifaddrs(ifList);
    return -1;
}

int get_local_gw(const char* eth_name ,char* gw)
{
    struct sockaddr_in *sin = NULL;
    struct ifaddrs *ifa = NULL, *ifList;

    if (getifaddrs(&ifList) < 0)
    {
        return -1;
    }

    for (ifa = ifList; ifa != NULL; ifa = ifa->ifa_next)
    {
        if(ifa->ifa_addr->sa_family == AF_INET)
        {
            if(strcmp(eth_name,ifa->ifa_name) == 0)
            {
                sin = (struct sockaddr_in *)ifa->ifa_dstaddr;
                strcpy(gw,inet_ntoa(sin->sin_addr));
                freeifaddrs(ifList);
                return 0;
            }
        }
    }

    freeifaddrs(ifList);
    return -1;    
}

int get_local_netmask(const char* eth_name ,char* netmask)
{
    struct sockaddr_in *sin = NULL;
    struct ifaddrs *ifa = NULL, *ifList;

    if (getifaddrs(&ifList) < 0)
    {
        return -1;
    }

    for (ifa = ifList; ifa != NULL; ifa = ifa->ifa_next)
    {
        if(ifa->ifa_addr->sa_family == AF_INET)
        {
            if(strcmp(eth_name,ifa->ifa_name) == 0)
            {
                sin = (struct sockaddr_in *)ifa->ifa_netmask;
                strcpy(netmask,inet_ntoa(sin->sin_addr));
                freeifaddrs(ifList);
                return 0;
            }
        }
    }

    freeifaddrs(ifList);
    return -1;
}

int get_ip_by_gw(const char* gw,char *ip)
{
    struct sockaddr_in *sin = NULL;
    struct ifaddrs *ifa = NULL, *ifList;

    if (getifaddrs(&ifList) < 0)
    {
        printf("out 1\n");
        return -1;
    }

    for (ifa = ifList; ifa != NULL; ifa = ifa->ifa_next)
    {
        if(ifa->ifa_addr->sa_family == AF_INET)
        {
            sin = (struct sockaddr_in *)ifa->ifa_dstaddr;
            #if DEBUG
            printf("gw: %s\n",inet_ntoa(sin->sin_addr));
            #endif
            if(strcmp(inet_ntoa(sin->sin_addr),gw) == 0)
            {
                sin = (struct sockaddr_in *)ifa->ifa_addr;
                if(ip) strcpy(ip,inet_ntoa(sin->sin_addr));
                freeifaddrs(ifList);
                return 0;
            }
        }
    }
    printf("out 2\n");
    freeifaddrs(ifList);
    return -1;
}

#endif  /* USE_IF_ADDR */
#endif  /* defined(_WIN32) || defined(_WIN64) */
